{---------------------------------------------------------------------
#
#	Apple Macintosh Developer Technical Support
#
#	MultiFinder-Aware Simple TextEdit Sample Application
#
#	OOPTESample
#
#	UApplication.inc1.p		-	Pascal Source
#
#	Copyright © 1988, 1989 Apple Computer, Inc.
#	All rights reserved.
#
#	Version:		
#					1.00					04/89
#
#	Components:     
#					BuildOOPTESample		April 1, 1989
#					MOOPTESample.p			April 1, 1989
#					OOPTESample.make		April 1, 1989
#					TECommon.h				April 1, 1989
#					TESampleGlue.a			April 1, 1989
#					TESample.r				April 1, 1989
#					UApplication.p			April 1, 1989
#					UApplication.inc1.p		April 1, 1989
#					UDocument.p				April 1, 1989
#					UDocument.inc1.p		April 1, 1989
#					UTEDocument.p			April 1, 1989
#					UTEDocument.inc1.p		April 1, 1989
#					UTESample.p				April 1, 1989
#					UTESample.inc1.p		April 1, 1989
#
---------------------------------------------------------------------}

CONST
	kOSEvent				= app4Evt;	{ Event used by MultiFinder }
	kSuspendResumeMessage	= $01;		{ high byte of suspend/resume event message }
	kClipConvertMask		= $02;		{ bit of message field clip conversion }
	kResumeMask				= $01;		{ bit of message field for resume vs. suspend }
	kMouseMovedMessage		= $FA;		{ high byte of mouse-moved event message }
	
	kErrStrings				= 128;
	rUserAlert				= 129;
	
	eWrongMachine			= 1;
	eSmallSize				= 2;


(********************************************************************************************)
(*		U t i l i t y   r o u t i n e s														*)
(********************************************************************************************)

{This routine is part of the MPW runtime library. This external
 reference to it is done so that we can unload its segment, %A5Init.}

PROCEDURE _DataInit;
	EXTERNAL;

{$S Main}
{-----------------------------------+
|	AlertUser						|
+-----------------------------------}
{ Display alert, using specified error STR# resource and error code as index }
PROCEDURE AlertUser(errResID:integer; errCode:integer);
VAR
	message: Str255;
	dummy: integer;
BEGIN
	SetCursor(arrow);
	GetIndString(message, errResID, errCode);
	ParamText(message, '', '', '');
	dummy := Alert(rUserAlert, NIL);
END;

{$S Main}
{-----------------------------------+
|	BigBadError						|
+-----------------------------------}
{ call AlertUser to display error message, then quit... }
PROCEDURE BigBadError(errResID:integer; errCode: integer);
BEGIN
	AlertUser(errResID,errCode);
	ExitToShell;
END;

{$S Initialize}
PROCEDURE InitSeg;
BEGIN
END;

(********************************************************************************************)
(*		T A p p l i c a t i o n																*)
(********************************************************************************************)
{$S Initialize}
{-----------------------------------+
|	IApplication					|
+-----------------------------------}
PROCEDURE TApplication.IApplication;

VAR
	envRec: sysEnvRec;
	stkNeeded, heapSize: longint;
	dummy: OSErr;

BEGIN
	{ initialize Mac Toolbox components }
	InitGraf(@thePort);
	InitFonts;
	InitWindows;
	InitMenus;
	TEInit;
	InitDialogs(NIL);
	InitCursor;

	{ Unload data segment: note that _DataInit must not be in Main! }
	UnloadSeg(@_DataInit);

	{ Ignore the error returned from SysEnvirons; even if an error occurred, }
	{ the SysEnvirons glue will fill in the SysEnvRec }
	dummy := SysEnvirons(curSysEnvVers, envRec);

	{ Are we running on a 128K ROM machine or better??? }
	IF (envRec.machineType < 0) THEN
	  BigBadError(kErrStrings,eWrongMachine);		{ if not, alert & quit }

	{ if we need more stack space, get it now }
	stkNeeded := StackNeeded;
	IF (stkNeeded > StackSpace) THEN BEGIN
		{ new address is heap size + current stack - needed stack }
		SetApplLimit(Ptr((longint(GetApplLimit) - stkNeeded + StackSpace)));
	END;
	
	{ Check for minimum heap size }
	heapSize := longint(GetApplLimit) - longint(ApplicZone);
	IF (heapSize < HeapNeeded) THEN
	  BigBadError(kErrStrings,eSmallSize);

	{ expand the heap so new code segments load at the top }
	MaxApplZone;

	{ allocate an empty document list }
	NEW(fDocList);
	fDocList.IDocumentList;

	{ check to see if WaitNextEvent is implemented }
	fHaveWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);

	{ initialize our class variables }
	fCurDoc := NIL;
	fDone := FALSE;
	fInBackground := FALSE;
	fMouseRgn := NIL;
	fWhichWindow := NIL;
END;

{$S Main}
{-----------------------------------+
|	EventLoop						|
+-----------------------------------}
PROCEDURE TApplication.EventLoop;
VAR
	gotEvent: Boolean;
	anEvent: EventRecord;
BEGIN

	SetUp;		{ call setup routine }
	DoIdle;		{ do idle once }

	WHILE (fDone = FALSE) DO BEGIN
	  {
		{ always set up fWhichWindow before doing anything }
		fWhichWindow := FrontWindow;

		{ see if window belongs to a document }
		fCurDoc := fDocList.FindDoc(fWhichWindow);

		{ make sure we always draw into correct window }
		SetPort(fWhichWindow);

		DoIdle;			{ call idle time handler }

		IF (fHaveWaitNextEvent) THEN BEGIN
			gotEvent := WaitNextEvent(everyEvent, anEvent, SleepVal, fMouseRgn);
		END ELSE BEGIN
			SystemTask;
			gotEvent := GetNextEvent(everyEvent, anEvent);
		END;
		fTheEvent := anEvent;

		{ make sure we got a real event }
		IF gotEvent THEN BEGIN
			AdjustCursor;
			CASE (fTheEvent.what) OF
				mouseDown	: HdlMouseDown;
				mouseUp		: HdlMouseUp;
				keyDown,
				autoKey		: HdlKeyDown;
				updateEvt	: HdlUpdateEvt;
				diskEvt		: HdlDiskEvt;
				activateEvt	: HdlActivateEvt;
				kOsEvent 	: HdlOSEvent;
			END; { end switch (fTheEvent.what) }
		END; { if gotEvent }
		AdjustCursor;
	END; {of EventLoop}
	CleanUp;
END;

{$S Main}
{-----------------------------------+
|	Setup							|
+-----------------------------------}
PROCEDURE TApplication.Setup;		{ Run before event loop starts }
BEGIN
END;

{$S Main}
{-----------------------------------+
|	CleanUp							|
+-----------------------------------}
PROCEDURE TApplication.CleanUp;		{ Run at end of loop }
BEGIN
	UnloadSeg(@InitSeg);
END;

{$S Main}
{-----------------------------------+
|	ExitLoop						|
+-----------------------------------}
PROCEDURE TApplication.ExitLoop;	{ Call this to exit loop }
BEGIN
	fDone := TRUE;
END;

{$S Main}
{-----------------------------------+
|	DoIdle							|
+-----------------------------------}
PROCEDURE TApplication.DoIdle;		{ Idle time handler (blink caret, background tasks) }
BEGIN
END;

{$S Main}
{-----------------------------------+
|	AdjustMenus						|
+-----------------------------------}
PROCEDURE TApplication.AdjustMenus;	{ Menu Updater routine }
BEGIN
END;

{$S Main}
{-----------------------------------+
|	HdlOSEvent						|
+-----------------------------------}
PROCEDURE TApplication.HdlOSEvent;		{ Calls DoSuspend, DoResume and DoIdle as apropos }
VAR
	doConvert: Boolean;
	evType: byte;
BEGIN

	{ is it a multifinder event? }
	evType := BAnd(BRotR(fTheEvent.message, 24),$00FF);
	CASE evType OF		{ high byte of message is type of event }
		kMouseMovedMessage :
			DoIdle;					{ mouse-moved is also an idle event }
		kSuspendResumeMessage : BEGIN
			doConvert := (BAnd(fTheEvent.message, kClipConvertMask) <> 0);
			fInBackground := (BAnd(fTheEvent.message, kResumeMask) = 0);
			IF (fInBackground) THEN
				DoSuspend(doConvert)
			ELSE
				DoResume(doConvert);
		END; { kSuspendResumeMessage }
	END; { CASE Statement }
END;

{$S Main}
{-----------------------------------+
|	HdlMouseDown					|
+-----------------------------------}
PROCEDURE TApplication.HdlMouseDown;	{ Calls DoContent, DoGrow, DoZoom, etc }
VAR
	mResult: Longint;
	partCode: integer;
	anEvent: EventRecord;
	aWindow: WindowPtr;
BEGIN
	partCode := FindWindow(fTheEvent.where, aWindow);
	fWhichWindow := aWindow;
	CASE partCode OF
		inSysWindow : MouseInSysWindow;
		inMenuBar : BEGIN
			AdjustMenus;
			mResult := MenuSelect(fTheEvent.where);
			IF (mResult <> 0) THEN
			  DoMenuCommand(HiWrd(mResult),LoWrd(mResult));
		END;
		inGoAway :
			DoGoAway;
		inDrag :
			DoDrag;
		inGrow :
			IF (fCurDoc <> NIL) THEN BEGIN
				anEvent := fTheEvent;
				fCurDoc.DoGrow(anEvent);
			END;
		inZoomIn,
		inZoomOut :
			IF ((TrackBox(fWhichWindow, fTheEvent.where, partCode)) AND
					(fCurDoc <> NIL)) THEN
			  	fCurDoc.DoZoom(partCode);
		inContent : { If window is not in front, make it so }
			IF (fWhichWindow <> FrontWindow) THEN
				SelectWindow(fWhichWindow)
			ELSE IF (fCurDoc <> NIL) THEN BEGIN
				anEvent := fTheEvent;
				fCurDoc.DoContent(anEvent);
			END;
	END;
END;

{$S Main}
{-----------------------------------+
|	HdlKeyDown						|
+-----------------------------------}
PROCEDURE TApplication.HdlKeyDown;		{ also called for autokey events }
VAR
	key: char;
	mResult: longint;
	anEvent: EventRecord;
BEGIN
	key := char(BAnd(fTheEvent.message, charCodeMask));
	IF ((BAnd(fTheEvent.modifiers, cmdKey) <> 0) AND (fTheEvent.what = keyDown)) THEN BEGIN
		{ only do command keys if we are not autokeying }
		AdjustMenus;					{ make sure menus are up to date }
		mResult := MenuKey(key);
		IF (mResult <> 0) THEN BEGIN	{ if it wasn't a menu key, pass it through }
			DoMenuCommand(HiWrd(mResult), LoWrd(mResult));
		END;
	END ELSE BEGIN
		IF (fCurDoc <> nil) THEN BEGIN
			anEvent := fTheEvent;
		  fCurDoc.DoKeyDown(anEvent);
		END;
	END;
END;

{$S Main}
{-----------------------------------+
|	HdlActivateEvt					|
+-----------------------------------}
PROCEDURE TApplication.HdlActivateEvt;	{ handles setup, and calls DoActivate (below) }
BEGIN
	{ event record contains window ptr }
	fWhichWindow := WindowPtr(fTheEvent.message);

	{ see if window belongs to a document }
	fCurDoc := fDocList.FindDoc(fWhichWindow);
	SetPort(fWhichWindow);

	IF (fCurDoc <> NIL) THEN BEGIN
		fCurDoc.DoActivate(BAnd(fTheEvent.modifiers, activeFlag) <> 0);
	END;
END;

{$S Main}
{-----------------------------------+
|	HdlUpdateEvt					|
+-----------------------------------}
PROCEDURE TApplication.HdlUpdateEvt;	{ handles setup, and calls DoUpdate (below) }
BEGIN
	{ event record contains window ptr }
	fWhichWindow := WindowPtr(fTheEvent.message);

	{ see if window belongs to a document }
	fCurDoc := fDocList.FindDoc(fWhichWindow);
	SetPort(fWhichWindow);

	IF (fCurDoc <> NIL) THEN BEGIN
		fCurDoc.DoUpdate;
	END;
END;

{$S Main}
{-----------------------------------+
|	HdlMouseUp						|
+-----------------------------------}
PROCEDURE TApplication.HdlMouseUp;
BEGIN
END;

{$S Main}
{-----------------------------------+
|	HdlDiskEvt						|
+-----------------------------------}
PROCEDURE TApplication.HdlDiskEvt;
BEGIN
END;

{$S Main}
{-----------------------------------+
|	MouseInSysWindow				|
+-----------------------------------}
PROCEDURE TApplication.MouseInSysWindow;
VAR
	anEvent: EventRecord;
BEGIN
	anEvent := fTheEvent;
	SystemClick(anEvent,fWhichWindow);
END;

{$S Main}
{-----------------------------------+
|	DoDrag							|
+-----------------------------------}
PROCEDURE TApplication.DoDrag;
BEGIN
	DragWindow(fWhichWindow, fTheEvent.where, screenBits.bounds);
END;

{$S Main}
{-----------------------------------+
|	DoGoAway						|
+-----------------------------------}
PROCEDURE TApplication.DoGoAway;
VAR
	aWindow: WindowPeek;
BEGIN
	IF (TrackGoAway(fWhichWindow, fTheEvent.where)) THEN BEGIN
		IF (fCurDoc <> NIL) THEN BEGIN
			fDocList.RemoveDoc(fCurDoc);
			fCurDoc.Free;	{TDocument.Free disposes of window}
		END ELSE BEGIN
			aWindow := WindowPeek(fWhichWindow);
			CloseDeskAcc(aWindow^.windowKind);
		END;
	END;
END;

{$S Main}
{-----------------------------------+
|	AdjustCursor					|
+-----------------------------------}
PROCEDURE TApplication.AdjustCursor;	{ cursor adjust routine, should setup mouseRgn }
BEGIN
END;

{$S Main}
{-----------------------------------+
|	DoMenuCommand					|
+-----------------------------------}
PROCEDURE TApplication.DoMenuCommand(menuID,menuItem: integer);
BEGIN
END;

{$S Main}
{-----------------------------------+
|	DoSuspend						|
+-----------------------------------}
PROCEDURE TApplication.DoSuspend(VAR doClipConvert:Boolean);
BEGIN
	doClipConvert := FALSE;
	IF (fCurDoc <> NIL) THEN
	  fCurDoc.DoActivate(NOT (fInBackground));
END;

{$S Main}
{-----------------------------------+
|	DoResume						|
+-----------------------------------}
PROCEDURE TApplication.DoResume(VAR doClipConvert:Boolean);
BEGIN
	doClipConvert := FALSE;
	IF (fCurDoc <> NIL) THEN
	  fCurDoc.DoActivate(NOT(fInBackground));
END;

{$S Initialize}
{-----------------------------------+
|	TrapAvailable					|
+-----------------------------------}
FUNCTION TApplication.TrapAvailable(tNumber:integer;tType:TrapType):Boolean;
BEGIN
	{ See if the trap exists. On 64K ROM machines, tType will be ignored. }
	TrapAvailable := NGetTrapAddress(tNumber, tType) <>
						GetTrapAddress(_Unimplemented);
END;

{$S Main}
{-----------------------------------+
|	DocList							|
+-----------------------------------}
FUNCTION TApplication.DocList:TDocumentList;
BEGIN
	DocList := fDocList;
END;

{$S Initialize}
{-----------------------------------+
|	StackNeeded						|
+-----------------------------------}
FUNCTION TApplication.StackNeeded: Longint;
BEGIN
	StackNeeded := 0;
END;

{$S Initialize}
{-----------------------------------+
|	HeapNeeded						|
+-----------------------------------}
FUNCTION TApplication.HeapNeeded: Longint;
BEGIN
	HeapNeeded := 0;
END;

{$S Main}
{-----------------------------------+
|	SleepVal						|
+-----------------------------------}
FUNCTION TApplication.SleepVal: LongInt;
BEGIN
	SleepVal := 0;
END;